#ifndef __MY_DATA__
#define __MY_DATA__

#include <mutex>
#include <mysql/mysql.h>
#include <cstring>
#include <cstdlib>
#include "util.hpp"

namespace aod
{
#define HOST "127.0.0.1"
#define USER "root"
#define PASS "Zh_shuoshuo20030206."
#define DATABASE_NAME "aod_system"
#define PORT 3306

#define INSERT_VIDEO "insert tb_video values(null, '%s', '%s', '%s', '%s', 0, 0);"
#define UPDATE_VIDEO "update tb_video set name = '%s', info = '%s' where id = '%d';"
#define DELETE_VIDEO "delete from tb_video where id = '%d';"
#define SELECTALL_VIDEO "select * from tb_video;"
#define SELECTONE_VIDEO "select * from tb_video where id = %d;"
#define SELECTLIKE_VIDEO "select * from tb_video where name like '%%%s%';"
#define UPDATE_LIKES "update tb_video set like_count = like_count + 1 where id = '%d';"
#define UPDATE_COLLECTS "update tb_video set collect_count = collect_count + 1 where id = '%d';"

#define INSERT_USER "insert tb_user values(null, '%s', '%s');"
#define SELECTONE_USER "select * from tb_user where username = '%s';"

    static MYSQL *MysqlInit()
    {
        MYSQL *mysql = mysql_init(nullptr);
        if (mysql == nullptr)
        {
            std::cerr << "init mysql instance failed!" << std::endl;
            return nullptr;
        }
        if (mysql_real_connect(mysql, HOST, USER, PASS, DATABASE_NAME, PORT, nullptr, 0) == nullptr)
        {
            std::cerr << "connect mysql server failed!" << std::endl;
            mysql_close(mysql);
            return nullptr;
        }
        mysql_set_character_set(mysql, "utf8");

        return mysql;
    }

    static void MysqlDestroy(MYSQL *mysql)
    {
        if (mysql != nullptr)
            mysql_close(mysql);
    }

    static bool MysqlQuery(MYSQL *mysql, const std::string &sql)
    {
        int ret = mysql_query(mysql, sql.c_str());
        if (ret != 0)
        {
            std::cout << sql << std::endl;
            std::cerr << mysql_error(mysql) << std::endl;
            return false;
        }

        return true;
    }

    class TableVideo
    {
    public:
        TableVideo()
        {
            _mysql = MysqlInit();
            if (_mysql == nullptr)
                exit(-1);
        }

        ~TableVideo()
        {
            MysqlDestroy(_mysql);
        }

        bool Insert(const Json::Value &video)
        {
            // id name info video image
            std::string sql;
            sql.resize(4096 + video["info"].asString().size()); // 防止简介过长
            if (video["name"].asString().size() == 0)
            {
                std::cerr << "name error!" << std::endl;
                return false;
            }

            // 将格式化的输出写入一个字符串中
            sprintf(&sql[0], INSERT_VIDEO, video["name"].asCString(),
                    video["info"].asCString(), video["video"].asCString(),
                    video["image"].asCString());

            return MysqlQuery(_mysql, sql);
        }

        bool Update(int video_id, const Json::Value &video)
        {
            std::string sql;
            sql.resize(4096 + video["info"].asString().size());
            sprintf(&sql[0], UPDATE_VIDEO, video["name"].asCString(),
                    video["info"].asCString(), video_id);

            return MysqlQuery(_mysql, sql);
        }

        bool Delete(int video_id)
        {
            char sql[1024];
            sprintf(sql, DELETE_VIDEO, video_id);

            return MysqlQuery(_mysql, sql);
        }

        bool SelectAll(Json::Value *videos)
        {
            _mutex.lock(); // 保护查询与结果保存到本地的过程
            bool ret = MysqlQuery(_mysql, SELECTALL_VIDEO);
            if (ret == false)
            {
                _mutex.unlock();
                return false;
            }

            // mysql_use_result函数也是覆盖式保存的，返回值类型是一个数组指针
            MYSQL_RES *res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                std::cerr << "mysql store result failed" << std::endl;
                _mutex.unlock();
                return false;
            }
            _mutex.unlock();

            int rows_num = mysql_num_rows(res);

            for (int i = 0; i < rows_num; i++)
            {
                MYSQL_ROW row = mysql_fetch_row(res);

                Json::Value video;
                video["id"] = atoi(row[0]);
                video["name"] = row[1];
                video["info"] = row[2];
                video["video"] = row[3];
                video["image"] = row[4];

                // 作为videos的数组元素
                videos->append(video);
            }
            mysql_free_result(res);

            return true;
        }

        bool SelectOne(int video_id, Json::Value *video)
        {
            char sql[1024];
            memset(sql, 0, sizeof sql);
            sprintf(sql, SELECTONE_VIDEO, video_id);

            _mutex.lock(); // 保护查询与结果保存到本地的过程
            bool ret = MysqlQuery(_mysql, sql);
            if (ret == false)
            {
                _mutex.unlock();
                return false;
            }

            // mysql_use_result函数也是覆盖式保存的
            MYSQL_RES *res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                std::cerr << "mysql store result failed" << std::endl;
                _mutex.unlock();
                return false;
            }
            _mutex.unlock();

            int rows_num = mysql_num_rows(res);
            if (rows_num != 1)
            {
                std::cerr << "have no data!" << std::endl;
                mysql_free_result(res);
                return false;
            }

            MYSQL_ROW row = mysql_fetch_row(res);
            (*video)["id"] = video_id;
            (*video)["name"] = row[1];
            (*video)["info"] = row[2];
            (*video)["video"] = row[3];
            (*video)["image"] = row[4];
            (*video)["like_count"] = row[5];
            (*video)["collect_count"] = row[6];
            mysql_free_result(res);

            return true;
        }

        bool SelectLike(const std::string &key, Json::Value *videos)
        {
            char sql[1024];
            memset(sql, 0, sizeof sql);
            sprintf(sql, SELECTLIKE_VIDEO, key.c_str());

            _mutex.lock();
            bool ret = MysqlQuery(_mysql, sql);
            if (ret == false)
            {
                std::cerr << "selectlike query failed" << std::endl;
                _mutex.unlock();
                return false;
            }

            // mysql_use_result函数也是覆盖式保存的
            MYSQL_RES *res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                std::cerr << "mysql store result failed" << std::endl;
                _mutex.unlock();
                return false;
            }
            _mutex.unlock();

            int rows_num = mysql_num_rows(res);

            for (int i = 0; i < rows_num; i++)
            {
                MYSQL_ROW row = mysql_fetch_row(res);

                Json::Value video;
                video["id"] = atoi(row[0]);
                video["name"] = row[1];
                video["info"] = row[2];
                video["video"] = row[3];
                video["image"] = row[4];
                videos->append(video);
            }
            mysql_free_result(res);

            return true;
        }

        // 点赞
        bool UpdateLikes(int video_id)
        {
            char sql[1024];
            sprintf(sql, UPDATE_LIKES, video_id);
            return MysqlQuery(_mysql, sql);
        }

        // 收藏
        bool UpdateCollects(int video_id)
        {
            char sql[1024];
            sprintf(sql, UPDATE_COLLECTS, video_id);
            return MysqlQuery(_mysql, sql);
        }

    private:
        MYSQL *_mysql;
        std::mutex _mutex;
    };

    // TableUser 类，用于管理用户数据在数据库中的操作，包括用户注册（插入数据）和用户登录（查询数据）
    class TableUser
    {
    public:
        TableUser()
        {
            _mysql = MysqlInit();
            if (_mysql == nullptr)
            {
                std::cerr << "Failed to initialize MySQL connection! Exiting..." << std::endl;
                exit(-1);
            }
        }

        ~TableUser()
        {
            MysqlDestroy(_mysql);
        }

        bool Insert(const Json::Value &user)
        {
            std::string sql;
            sql.resize(1024);

            // 检查用户提供的用户名或密码是否为空
            if (user["username"].asString().size() == 0 || user["password"].asString().size() == 0)
            {
                std::cerr << "username or password error! Registration failed." << std::endl;
                return false;
            }

            sprintf(&sql[0], INSERT_USER, user["username"].asCString(), user["password"].asCString());

            return MysqlQuery(_mysql, sql);
        }

        // 根据用户名查询用户信息，用于用户登录验证
        bool SelectOne(const std::string &username, Json::Value *user)
        {
            char sql[1024];
            memset(sql, 0, sizeof sql);
            sprintf(sql, SELECTONE_USER, username.c_str());

            _mutex.lock();

            bool ret = MysqlQuery(_mysql, sql);
            if (ret == false)
            {
                _mutex.unlock();
                return false;
            }

            // 获取查询结果集，若失败则输出错误信息
            MYSQL_RES *res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                std::cerr << "mysql store result failed" << std::endl;
                _mutex.unlock();
                return false;
            }
            _mutex.unlock();

            // 获取结果集中的行数
            int rows_num = mysql_num_rows(res);
            if (rows_num != 1)
            {
                std::cerr << "have no data! User not found." << std::endl;
                mysql_free_result(res);
                return false;
            }

            // 获取结果集中的一行数据
            MYSQL_ROW row = mysql_fetch_row(res);
            (*user)["id"] = atoi(row[0]);
            (*user)["username"] = row[1];
            (*user)["password"] = row[2];

            mysql_free_result(res);

            return true;
        }

    private:
        MYSQL *_mysql;
        std::mutex _mutex;
    };
}

#endif
